/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 *
 * This source code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package local.media;


//import java.io.*;
import java.lang.Math;


/** Generates a single tone.
 */
public class ToneInputStream extends java.io.InputStream {
    /** The number of bytes that are notified as available */
    static int MAX_AVAILABLE_BYTES = 65536;

    /** Identifier of linear unsigned PCM */
    public static final int PCM_LINEAR_UNSIGNED = 0;

    /** Identifier of linear signed PCM */
    public static final int PCM_LINEAR_SIGNED = 1;


    /** Tone frequence */
    int f0;
    /** Tone ampliture in the interval [0:2^(n-1)] where n is the sample sinze in bits.  */
    double A;
    /** Offset to be added in case uo unsigned PCM */
    double zero;
    /** Sample rate [samples per seconds] */
    int fs;
    /** Sample size [bytes] */
    int size;
    /** Whether use big endian foramt */
    boolean big_endian;

    /** B=2*Pi*f0/fs */
    double B;
    /** Sample sequence number */
    double k;
    /** Buffer containing the current sample */
    byte[] s_buff;
    /** Index within s_buff */
    int s_index;

    /** Creates a new 8-bit per sample ToneInputStream */
   /*public ToneInputStream(int frequence, double ampliture, int sample_rate, int codec)
   {  init(frequence,ampliture,sample_rate,1,codec);
   }*/

    /** Creates a new ToneInputStream */
    public ToneInputStream(int frequence, double ampliture, int sample_rate, int sample_size, int codec, boolean big_endian)
    {  init(frequence,ampliture,sample_rate,sample_size,codec,big_endian);
    }

    /** Inits the ToneInputStream */
    private void init(int frequence, double ampliture, int sample_rate, int sample_size, int codec, boolean big_endian)
    {  this.f0=frequence;
        this.fs=sample_rate;
        this.size=sample_size;
        this.big_endian=big_endian;
        B=(2*Math.PI*f0)/fs;
        long range=((long)1)<<((sample_size*8)-1);
        A=ampliture*range;
        if (codec==PCM_LINEAR_SIGNED) zero=0.0F;
        else zero=range/2;
        k=0;
        s_index=0;
        s_buff=new byte[size];

        //System.out.println("Tone: PI: "+Math.PI);
        //System.out.println("Tone: sin(PI/6): "+Math.sin(Math.PI/6));
        //System.out.println("Tone: s_rate: "+fs);
        //System.out.println("Tone: s_size: "+size);
        //System.out.println("Tone: A: "+A);
        //System.out.println("Tone: 0: "+zero);
    }


    /** Returns the number of bytes that can be read (or skipped over) from this input stream without blocking by the next caller of a method for this input stream. */
    public int available()
    {  return MAX_AVAILABLE_BYTES;
    }


    /** Reads the next sample. */
    private double nextSample()
    {  return A*Math.sin(B*(k++))+zero;
    }

    /** Reads the next byte of data from the input stream. */
    public int read()
    {  if (s_index==0)
    {  // get next sample
        long next_sample=(long)(nextSample());
        // set the s_buff
        for (int i=0; i<size; i++)
        {  int pos=(big_endian)? size-i-1 : i ;
            s_buff[pos]=(byte)((next_sample/(((long)1)<<(i*8)))%256);
        }
    }
        int b=s_buff[s_index];
        s_index=(++s_index)%size;
        return b;
    }


    /** Reads some number of bytes from the input stream and stores them into the buffer array b. */
    public int read(byte[] b)
    {  return read(b,0,b.length);
    }

    /** Reads up to len bytes of data from the input stream into an array of bytes. */
    public int read(byte[] b, int off, int len)
    {  for (int i=off; i<off+len; i++)
    {  b[i]=(byte)read();
    }
        return len;
    }

    /** Skips over and discards n bytes of data from this input stream. */
    public long skip(long n)
    {  // to do..
        return 0;
    }

    /** Closes this input stream and releases any system resources associated with the stream. */
    public void close()
    {  // do nothing
    }

    /** Tests if this input stream supports the mark and reset methods. */
    public boolean markSupported()
    {  return false;
    }

    /** Marks the current position in this input stream. */
    public void mark(int readlimit)
    {
    }

    /** Repositions this stream to the position at the time the mark method was last called on this input stream. */
    public void reset()
    {
    }


}


